program fpdocp;

{*
fpdocp = fpdoc preprocessor (documentation is stored internaly in documented source code)

TODO: It doesn't generate documentation from DPR file!
TODO: It seems like dglobals.mo are in fpdoc converted 2 times hence localized texts doesn't really work
TODO: -ee switch support - generate list of files from other directories and use them
}

{$I configs.inc}

uses
  SysUtils,
  Classes,
  Parsers,
  Utils,
{$IFDEF FPC}Process{$ELSE}ProcessDelphi{$ENDIF},
  Keywords;

var
  poms, PackageName, SourceDirectory, FpdocCommand, Lang, FileName, OutFileName, FileListFileName, OutputDir: string;
  Header, Footer, WaitOnExit, Verbose, VeryVerbose, ExecuteFpdoc, EE: boolean;
  Sources: TArrayOfString;
  Xml, fl: TStringList;
  i: integer;

procedure AddHeader(AOut: TStrings; APackage: string);
{*
Add header of fpdoc xml file
}
begin
  AOut.Add('<?xml version="1.0" encoding="UTF-8"?>');
  AOut.Add('<fpdoc-description>');
  AOut.Add('  <package name="' + APackage + '">');
end;

procedure AddFooter(AOut: TStrings);
{*
Add footer of fpdoc xml file
}
begin
  AOut.Add('  </package>');
  AOut.Add('</fpdoc-description>');
end;

procedure AddModule(AOut: TStrings; ASource: string);
{*
Add module to fpdoc xml file
}
begin
  AOut.Add('<!-- module from "' + ASource + '" -->');
end;

procedure LoadSources;
{*
Load list of source files (pas,dpr) from given source directory
}
var
  sr: TSearchRec;
  i, j: integer;
begin
  i := FindFirst(SourceDirectory + '*.*', faAnyFile - faDirectory, sr);
  j := 0;
  while i = 0 do
  begin
    if (LowerCase(ExtractFileExt(sr.Name)) = '.pas')
      or (LowerCase(ExtractFileExt(sr.Name)) = '.dpr') then
    begin
      SetLength(Sources, high(Sources) + 2);
      Sources[j] := SourceDirectory + sr.Name;
      fl.Add(Sources[j]);
      inc(j);
    end;
    i := FindNext(sr);
  end;
  FindClose(sr);
end;

begin
  // get input parameters
  PackageName := ParamSwitchValue('-p');
  OutFileName := ParamSwitchValue('-o');
  Lang := ParamSwitchValue('-lang');
  OutputDir := ParamSwitchValue('-d');
  if not DirectoryExists(OutputDir) then
    CreateDir(OutputDir);
  SetLength(Sources, 0);
  StrToArray(ParamSwitchValues('-s', ',', '-'), ',', Sources);
  if ParamSwitchValue('-s') <> '' then
    if High(Sources) < 0 then
    begin
      writeln('ERROR: Using -s switch but no sources was added!');
      halt(1);
    end;
  Verbose := ParamSwitchExists('-v');
  VeryVerbose := ParamSwitchExists('-vv');
  if VeryVerbose then
    Verbose := true;
  if Verbose then
    writeln('Switch -s added ', High(Sources) + 1, ' stand alone sources');
  Header := ParamSwitchExists('-h');
  FileListFileName := ParamSwitchValue('-l');
  fl := TStringList.Create;
  if FileExists(FileListFileName) then
    fl.LoadFromFile(FileListFileName);
  SourceDirectory := ParamSwitchValue('-r');
  if SourceDirectory = '.' then
    SourceDirectory := GetCurrentDir;
  SourceDirectory := SourceDirectory + PathDelim;
  if SourceDirectory <> '' then
  begin
    SetLength(Sources, 0);
    LoadSources;
    if Length(Sources) = 0 then
    begin
      writeln('error: no sources found in ', SourceDirectory);
      halt;
    end;
  end;
  ExecuteFpdoc := ParamSwitchExists('-e');
  EE := ParamSwitchExists('-ee');
  if EE then
  begin
    // in EE mode use file list for sources
    SetLength(Sources, fl.Count);
    for i := 0 to fl.Count - 1 do
      Sources[i] := fl[i];
  end;
  Footer := ParamSwitchExists('-f');
  WaitOnExit := ParamSwitchExists('-w');
  // check input parameters
  if (PackageName = '') or ((High(Sources) < 0) and (not Header) and (not Footer)) then
  begin
    writeln('fpdocp [-p <package>] [-s <sourceDirectory>] [-v[v]] [-c] [-w]');
    writeln('-p <package>            - define package name');
    writeln('-r <path>               - use path instead of explicit files');
    writeln('-s <source>             - add 1 source file, can be used multiple times');
    writeln('-o <OutFileName>        - define output file name');
    writeln('-d <OutputDir>          - define output directory');
    //writeln('-s <sourceDirectory>    - define source directory, if not defined, current directory will be used');
    writeln('-h                      - include header');
    writeln('-f                      - include footer');
    writeln('-l                      - generate file list (or append it to that file)');
    writeln('-lang <code>            - use other language for fpdoc');
    writeln('-w                      - wait for ENTER on exit');
    writeln('-v                      - verbose');
    writeln('-vv                     - very verbose');
    writeln('-c                      - create fpdoci.ini by given parameters');
    writeln('-e                      - execute fpdoc afterwards');
    writeln('-ee                     - execute fpdoc only using file list');
    writeln('-k <KeywordsFile>       - path to file with keywords');
    halt(1);
  end;
  // vytvorime xml dokumentacny subor
  if not ee then
  begin
    xml := TStringList.Create;
    if Header then
      AddHeader(xml, PackageName);
    // add sources
    for i := 0 to High(Sources) do
    begin
      if Verbose then
        writeln('Source[', i, ']: ', Sources[i]);
      FileName := Sources[i];
      with TParser.Create do
      try
        LoadFromFile(FileName);
        Text := Parse(ChangeFileExt(ExtractFileName(FileName), ''));
        xml.Add(Text);
        Free;
      except
        Free;
        raise;
      end;
    end;
    // add footer
    if footer then
      AddFooter(xml);
    if OutFileName <> '' then
      xml.SaveToFile(OutFileName);
    // vypiseme xml na obrazovku
    if Verbose then
      writeln(xml.text);
    if Verbose then
      writeln('==Saving file list!');
    fl.SaveToFile(FileListFileName);
  end;
  // execute original fpdoc
  if ExecuteFpdoc or EE then
  begin
    poms := '';
    if Lang <> '' then
      poms := '--lang="' + Lang + '"';
    FpdocCommand := 'fpdoc ' + poms + ' --package="' + PackageName + '" --content --output=' + OutputDir + ' --descr=' + OutFileName;
    for i := 0 to high(Sources) do
    begin
      if not FileExists(Sources[i]) then
        writeln('error: file not found - "' + Sources[i] + '"');
      FpdocCommand := FpdocCommand + ' --input=' + Sources[i];
    end;
    //writeln(FpdocCommand);
    with TProcess.Create(nil) do
    try
      poms := 'C';
      if WaitOnExit then
        poms := 'K';
      CommandLine := 'cmd /' + poms + ' ' + FpdocCommand;
      if Verbose then
        writeln('CommandLine = ', CommandLine);
      {with TStringList.Create do
      begin
        Text := CommandLine;
        SaveTOFile('zzz');
        Free;
      end;}
      Options := [poWaitOnExit];
      Execute;
      if Verbose then
        writeln('ExitStatus = ', ExitStatus);
      Free;
    except
      Free;
      raise;
    end;
  end;
  // finalize
  if WaitOnExit then
  begin
    writeln('Press enter to exit');
    readln;
  end;
end.

